package com.example.demo.utils;

import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;

import java.util.HashMap;

@Component
@ConditionalOnExpression("${common-application.start:false}")
public class IdGenerator {


    //机器Id位数
    private final long workerIdBits = 5L;
    //机房id位数
    private final long datacenterIdBits = 5L;
    // 机器Id的最大值
    private final long maxWorkerId = ~(-1L << workerIdBits);
    // 机房Id的最大值
    private final long maxDatacenterId = ~(-1L << datacenterIdBits);

    //机器Id 5bit
    private final long workerId;
    //机房Id 5bit
    private final long datacenterId;

    public IdGenerator() {
        this.workerId = 0;
        this.datacenterId = 0;
    }

    public IdGenerator(long datacenterId, long workerId) {
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    //代表一毫秒内生成id的最新序号 12bit
    private final HashMap<Class<?>, Long> sequenceMap = new HashMap<>();

    //已经产生过的时间毫秒数，判断是否是同1毫秒
    private final HashMap<Class<?>, Long> lastTimestampMap = new HashMap<>();

    public synchronized long nextId(Class<?> entityClz) {
        long currentTimestamp = System.currentTimeMillis();
        long lastTimestamp = lastTimestampMap.getOrDefault(entityClz, -1L);
        long sequence;
        if(currentTimestamp < lastTimestamp)
            throw new RuntimeException("Clock moved backwards.");
        //代表一毫秒内生成id的最新序号的位数
        long sequenceBits = 12L;
        if(currentTimestamp == lastTimestamp) {
            sequence = sequenceMap.getOrDefault(entityClz, 0L);
            long sequenceMask = ~(-1L << sequenceBits);
            sequence = (sequence+1) & sequenceMask;
            if(sequence == 0) {
                tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestampMap.put(entityClz, currentTimestamp);
        sequenceMap.put(entityClz, sequence);
        //设置一个时间初始值2020/10/27 10:49 41bit (41bit时间戳可以用69年)
        long startEpoch = 1603766739000L;
        // 各部分需要左移的位数
        long workerIdShift = sequenceBits;
        long datacenterIdShift = workerIdShift + workerIdBits;
        long timestampLeftShift = datacenterIdShift + datacenterIdBits;
        return  ((currentTimestamp - startEpoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }



    @SuppressWarnings("UnusedReturnValue")
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;

    }

}
